package cn.hg.solon.youcan.system.provider;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import org.dromara.hutool.core.bean.BeanUtil;
import org.dromara.hutool.core.bean.copier.CopyOptions;
import org.dromara.hutool.core.collection.CollUtil;
import org.dromara.hutool.core.collection.ListUtil;
import org.dromara.hutool.core.convert.Convert;
import org.dromara.hutool.core.text.CharSequenceUtil;
import org.dromara.hutool.core.text.StrValidator;
import org.dromara.hutool.core.util.ObjUtil;
import org.dromara.hutool.db.PageResult;
import org.noear.solon.annotation.Component;
import org.noear.solon.data.annotation.Tran;

import com.easy.query.api4j.client.EasyQuery;
import com.easy.query.api4j.select.Queryable;
import com.easy.query.core.api.pagination.EasyPageResult;
import com.easy.query.solon.annotation.Db;

import cn.hg.solon.youcan.common.constant.AppConstants;
import cn.hg.solon.youcan.common.constant.WebConstants;
import cn.hg.solon.youcan.common.enums.BeanStatus;
import cn.hg.solon.youcan.common.exception.ServiceException;
import cn.hg.solon.youcan.system.entity.EqRole;
import cn.hg.solon.youcan.system.entity.EqRoleDeptMapping;
import cn.hg.solon.youcan.system.entity.EqRolePermissionMapping;
import cn.hg.solon.youcan.system.entity.EqUser;
import cn.hg.solon.youcan.system.entity.EqUserRoleMapping;
import cn.hg.solon.youcan.system.entity.Role;
import cn.hg.solon.youcan.system.entity.User;
import cn.hg.solon.youcan.system.service.RoleService;

/**
 * @author 胡高
 */
@Component
public class EqRoleProvider implements RoleService {

    @Db
    private EasyQuery easyQuery;

    /* (non-Javadoc)
     * @see cn.hg.solon.youcan.system.service.RoleService#assignDataScope(cn.hg.solon.youcan.system.entity.Role, java.lang.String, java.util.List)
     */
    @Tran
    @Override
    public void assignDataScope(Role role, String dataScope, List<Integer> deptIds) {
        /*
         * 删除所有关联部门
         * DELETE FROM sys_role_dept_mapping WHERE role_id = ${role.id}
         */
        this.easyQuery.deletable(EqRoleDeptMapping.class)
        .where(w -> w.eq(EqRoleDeptMapping::getRoleId, role.getId()))
        .allowDeleteStatement(true)
        .executeRows();

        /*
         * 重新建立角色部门映射
         */
        List<EqRoleDeptMapping> beanList = new ArrayList<>();
        for (int id : deptIds) {
            EqRoleDeptMapping bean = new EqRoleDeptMapping();
            bean.setRoleId(role.getId());
            bean.setDeptId(id);

            beanList.add(bean);
        }

        if (CollUtil.isNotEmpty(beanList)) {
            this.easyQuery.insertable(beanList).executeRows();
        }

        role.setDataScope(dataScope);

        this.easyQuery.updatable(role).executeRows();
    }

    /* (non-Javadoc)
     * @see cn.hg.solon.youcan.system.service.RoleService#assignPermission(cn.hg.solon.youcan.system.entity.Role, java.util.List)
     */
    @Tran
    @Override
    public void assignPermission(Role role, List<Integer> permissionIds) {
        /*
         * 删除所有关联权限
         * DELETE FROM sys_role_permission_mapping WHERE role_id = ${role.id}
         */
        this.easyQuery.deletable(EqRolePermissionMapping.class)
        .where(w -> w.eq(EqRolePermissionMapping::getRoleId, role.getId()))
        .allowDeleteStatement(true)
        .executeRows();

        /*
         * 重新建立角色权限映射
         */
        List<EqRolePermissionMapping> beanList = new ArrayList<>();
        for (int id : permissionIds) {
            EqRolePermissionMapping bean = new EqRolePermissionMapping();
            bean.setRoleId(role.getId());
            bean.setPermissionId(id);

            beanList.add(bean);
        }

        if (CollUtil.isNotEmpty(beanList)) {
            this.easyQuery.insertable(beanList).executeRows();
        }
    }

    private Queryable<EqRole> buildQuery(Map<String, Object> paraMap) {
        String word = (String)paraMap.get("word");
        String status = (String)paraMap.get("status");
        String sortField = (String)paraMap.get("sortField");
        String sortType = (String)paraMap.get("sortType");

        boolean customOrder = StrValidator.isNotBlank(sortField) && StrValidator.isNotBlank(sortType);

        // FROM sys_role AS t
        return this.easyQuery.queryable(EqRole.class)
            // WHERE t.`status` = ${status}
            //      AND (t.`name` LIKE '%${word}%' OR t.`code` LIKE '%${word}%' OR t.`value` LIKE '%${word}%')
            .where(w -> w.eq(StrValidator.isNotBlank(status), EqRole::getStatus, status)
                .and(StrValidator.isNotBlank(word),
                    a -> a.like(EqRole::getName, word)
                    .or().like(EqRole::getCode, word)))
            // ORDER BY ${sortField} DESC
            .orderBy(customOrder, ob -> ob.sqlNativeSegment(CharSequenceUtil.toUnderlineCase(sortField)),
                !CharSequenceUtil.equalsIgnoreCase(sortType, WebConstants.ORDER_DIRECTION_DESC));
    }

    /* (non-Javadoc)
     * @see cn.hg.solon.youcan.system.service.RoleService#checkUnique(cn.hg.solon.youcan.system.entity.Role)
     */
    @Override
    public boolean checkUnique(Role bean) {
        // FROM sys_role AS t
        return !this.easyQuery.queryable(EqRole.class)
            // WHERE t.`code` = ${bean.code} AND t.`id` <> ${bean.id}
            .where(w -> w.eq(EqRole::getCode, bean.getCode())
                .ne(ObjUtil.isNotNull(bean.getId()), EqRole::getId, bean.getId()))
            .any();
    }

    /* (non-Javadoc)
     * @see cn.hg.solon.youcan.system.service.RoleService#delete(java.util.List)
     */
    @Tran
    @Override
    public boolean delete(List<Integer> idList) {
        if (CollUtil.contains(idList, Integer.valueOf(1))) {
            throw new ServiceException(AppConstants.RETURN_CODE_VALUE_FAILED, "初始的 超级管理员角色 不允许删除！");
        }

        /*
         * 检查是否已分配于用户
         */
        for (Integer id : idList) {
            if (// FROM sys_role AS t
                this.easyQuery.queryable(EqRole.class)
                // LEFT JOIN sys_user_role_mapping AS t1 ON t1.`role_id` = t.`id`
                .leftJoin(EqUserRoleMapping.class, (r, ur) -> ur.eq(r, EqUserRoleMapping::getRoleId, EqRole::getId))
                // LEFT JOIN sys_user AS t2 ON t2.`id` = t1.`user_id`
                .leftJoin(EqUser.class, (r, ur, u) -> u.eq(ur, EqUser::getId, EqUserRoleMapping::getUserId))
                // WHERE t2.`role_id` = ${id}
                .where((r, ur, u) -> ur.eq(EqUserRoleMapping::getRoleId, id))
                .where((r, ur, u) -> u.eq(EqUser::getDel, Boolean.FALSE))
                .any()) {
                throw new ServiceException(AppConstants.RETURN_CODE_VALUE_FAILED, "已分配于用户，不允许删除！");
            }
        }

        return this.easyQuery.deletable(EqRole.class).whereByIds(idList).executeRows() > 0;
    }

    /* (non-Javadoc)
     * @see cn.hg.solon.youcan.system.service.RoleService#get(java.lang.Integer)
     */
    @Override
    public Role get(Integer id) {
        return this.easyQuery.queryable(EqRole.class).whereById(id).firstOrNull();
    }

    /* (non-Javadoc)
     * @see cn.hg.solon.youcan.system.service.RoleService#getByCode(java.lang.String)
     */
    @Override
    public Role getByCode(String code) {
        // FROM sys_role AS t
        return this.easyQuery.queryable(EqRole.class)
            // WHERE t.`status` = 'on' AND t.`code` = ${code}
            .where(w -> w.eq(EqRole::getStatus, BeanStatus.ON.name()).eq(EqRole::getCode, code))
            .firstOrNull();
    }

    /* (non-Javadoc)
     * @see cn.hg.solon.youcan.system.service.RoleService#insert(cn.hg.solon.youcan.system.entity.Role, java.util.List, java.util.List)
     */
    @Tran
    @Override
    public boolean insert(Role bean, List<Integer> deptIds, List<Integer> permissionIds) {
        if (!this.checkUnique(bean)) {
            throw new ServiceException(AppConstants.RETURN_CODE_VALUE_FAILED, "权限代码已经存在，请更换其它值！");
        }

        EqRole cloneBean = BeanUtil.copyProperties(bean, EqRole.class);

        // 注意：插入时使用的True，代表插入同时获取ID值填充到cloneBean
        boolean ret = this.easyQuery.insertable(cloneBean).executeRows(true) > 0L;

        // 指派数据权限
        this.assignDataScope(cloneBean, bean.getDataScope(), deptIds);

        // 指派系统权限
        this.assignPermission(cloneBean, permissionIds);

        return ret;
    }

    /* (non-Javadoc)
     * @see cn.hg.solon.youcan.system.service.RoleService#listBy(java.util.Map)
     */
    @Override
    public List<? extends Role> listBy(Map<String, Object> paraMap) {
        return this.easyQuery.queryable(EqRole.class).toList();
    }

    /* (non-Javadoc)
     * @see cn.hg.solon.youcan.system.service.RoleService#listByStatus(java.lang.String)
     */
    @Override
    public List<? extends Role> listByStatus(String status) {
        // FROM sys_role AS t
        return this.easyQuery.queryable(EqRole.class)
            // WHERE t.`status` = ${status}
            .where(w -> w.eq(StrValidator.isNotBlank(status), EqRole::getStatus, status))
            // ORDER BY t.`sort` ASC
            .orderBy(ob -> ob.column(EqRole::getSort), true)
            .toList();
    }

    /* (non-Javadoc)
     * @see cn.hg.solon.youcan.system.service.RoleService#listByUser(cn.hg.solon.youcan.system.entity.User)
     */
    @Override
    public List<? extends Role> listByUser(User user) {
        if (ObjUtil.isNull(user)) {
            return ListUtil.empty();
        }

        // FROM sys_role AS t
        return this.easyQuery.queryable(EqRole.class)
            // LEFT JOIN sys_user_role AS t1 ON t1.`role_id` = t.`id`
            .leftJoin(EqUserRoleMapping.class, (u, ur) -> ur.eq(u, EqUserRoleMapping::getRoleId, EqRole::getId))
            // WHERE t.`status` = 'ON' AND t1.`user_id` = ${user.id}
            .where((u, ur) -> u.eq(EqRole::getStatus, BeanStatus.ON.name())
                .then(ur.eq(EqUserRoleMapping::getUserId, user.getId())))
            .toList();
    }

    /* (non-Javadoc)
     * @see cn.hg.solon.youcan.system.service.RoleService#pageBy(int, int, java.util.Map)
     */
    @Override
    public PageResult<EqRole> pageBy(int pageNumber, int pageSize, Map<String, Object> paraMap) {
        EasyPageResult<EqRole> pageList = this.buildQuery(paraMap).toPageResult(Convert.toLong(pageNumber), pageSize);

        PageResult<EqRole> result = new PageResult<>();
        result.addAll(pageList.getData());
        result.setTotal(Convert.toInt(pageList.getTotal()));

        return result;
    }

    /* (non-Javadoc)
     * @see cn.hg.solon.youcan.system.service.RoleService#update(cn.hg.solon.youcan.system.entity.Role)
     */
    @Tran
    @Override
    public boolean update(Role bean) {
        if (!this.checkUnique(bean)) {
            throw new ServiceException(AppConstants.RETURN_CODE_VALUE_FAILED, "权限代码已经存在，请更换其它值！");
        }

        EqRole cloneBean = (EqRole)this.get(bean.getId());

        BeanUtil.copyProperties(bean, cloneBean, CopyOptions.of().setIgnoreNullValue(true));

        return this.easyQuery.updatable(cloneBean).executeRows() > 0;
    }

}
